package com.xhj.mybatis.mapping;

import com.xhj.mybatis.cache.Cache;
import com.xhj.mybatis.executor.keygen.Jdbc3KeyGenerator;
import com.xhj.mybatis.executor.keygen.KeyGenerator;
import com.xhj.mybatis.executor.keygen.NoKeyGenerator;
import com.xhj.mybatis.scripting.LanguageDriver;
import com.xhj.mybatis.session.Configuration;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * MappedStatement
 *
 * @author XJks
 * @description SQL 映射语句的封装类，包含了 SQL 语句及其相关的配置信息
 */
public class MappedStatement {

    /**
     * 全局配置对象
     */
    private Configuration configuration;

    /**
     * 资源路径。
     */
    private String resource;

    /**
     * MappedStatement 的唯一标识符
     */
    private String id;

    /**
     * SQL 语句的类型（如 SELECT、INSERT、UPDATE、DELETE）
     */
    private SqlCommandType sqlCommandType;

    /**
     * SQL 语句的源对象，包含了 SQL 语句及其参数信息
     */
    private SqlSource sqlSource;

    /**
     * TODO:
     *  1.缓存对象，用于二级缓存查询结果。
     *  2.每个 XxxMapper.class 对应一个 Cache 实例。
     */
    private Cache cache;

    /**
     * ResultMap 列表，定义了 SQL 查询结果与 Java 对象属性之间的映射关系
     */
    private List<ResultMap> resultMaps;

    /**
     * TODO:是否需要在执行语句前刷新缓存
     */
    private boolean flushCacheRequired;

    /**
     * 是否启用二级缓存
     */
    private boolean useCache;

    /**
     * 用于生成主键的策略对象，比如自增主键或自定义主键生成器
     */
    private KeyGenerator keyGenerator;

    /**
     * 用于存储主键属性名的数组，通常对应数据库表的主键字段名
     */
    private String[] keyProperties;

    /**
     * 用于存储主键列名的数组，通常对应数据库表的主键列名
     */
    private String[] keyColumns;

    /**
     * 数据库标识符（可选），用于支持多数据库环境
     */
    private String databaseId;

    /**
     * 指定当前 MappedStatement 使用的脚本语言驱动（如 XML、注解等），负责解析 SQL 语句。
     * LanguageDriver 是 MyBatis 的一个接口，不同实现可以支持不同的 SQL 编写方式。
     * 例如，默认是 XMLLanguageDriver，支持 XML 格式的 SQL 映射。
     * 这个字段在构建 MappedStatement 时由配置对象赋值，决定 SQL 的解析和执行方式。
     */
    private LanguageDriver lang;

    MappedStatement() {
        // constructor disabled
    }

    /**
     * 建造者
     */
    public static class Builder {

        private MappedStatement mappedStatement = new MappedStatement();

        public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
            mappedStatement.configuration = configuration;
            mappedStatement.id = id;
            mappedStatement.sqlCommandType = sqlCommandType;
            mappedStatement.sqlSource = sqlSource;
            mappedStatement.resultMaps = new ArrayList<>();
            mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
            mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance();
        }

        public Builder resource(String resource) {
            mappedStatement.resource = resource;
            return this;
        }

        public Builder resultMaps(List<ResultMap> resultMaps) {
            mappedStatement.resultMaps = resultMaps;
            return this;
        }

        public Builder cache(Cache cache) {
            mappedStatement.cache = cache;
            return this;
        }

        public Builder flushCacheRequired(boolean flushCacheRequired) {
            mappedStatement.flushCacheRequired = flushCacheRequired;
            return this;
        }

        public Builder useCache(boolean useCache) {
            mappedStatement.useCache = useCache;
            return this;
        }

        public Builder keyGenerator(KeyGenerator keyGenerator) {
            mappedStatement.keyGenerator = keyGenerator;
            return this;
        }

        public Builder keyProperty(String keyProperty) {
            mappedStatement.keyProperties = delimitedStringToArray(keyProperty);
            return this;
        }

        public Builder keyColumn(String keyColumn) {
            mappedStatement.keyColumns = delimitedStringToArray(keyColumn);
            return this;
        }

        public Builder databaseId(String databaseId) {
            mappedStatement.databaseId = databaseId;
            return this;
        }

        public Builder lang(LanguageDriver driver) {
            mappedStatement.lang = driver;
            return this;
        }

        public MappedStatement build() {
            assert mappedStatement.configuration != null;
            assert mappedStatement.id != null;
            assert mappedStatement.sqlSource != null;
            assert mappedStatement.lang != null;
            mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
            return mappedStatement;
        }

    }

    public String getResource() {
        return resource;
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    public String getId() {
        return id;
    }

    public SqlCommandType getSqlCommandType() {
        return sqlCommandType;
    }

    public SqlSource getSqlSource() {
        return sqlSource;
    }

    public List<ResultMap> getResultMaps() {
        return resultMaps;
    }

    public KeyGenerator getKeyGenerator() {
        return keyGenerator;
    }

    public String[] getKeyProperties() {
        return keyProperties;
    }

    public String[] getKeyColumns() {
        return keyColumns;
    }

    public Cache getCache() {
        return cache;
    }

    public boolean isFlushCacheRequired() {
        return flushCacheRequired;
    }

    public boolean isUseCache() {
        return useCache;
    }

    public String getDatabaseId() {
        return databaseId;
    }

    public LanguageDriver getLang() {
        return lang;
    }

    /**
     * 根据传入的参数对象，生成对应的 BoundSql（绑定 SQL）对象。
     * BoundSql 包含了最终要执行的 SQL 语句和参数映射信息。
     *
     * @param parameterObject 传入的参数对象
     * @return 生成的 BoundSql 对象
     */
    public BoundSql getBoundSql(Object parameterObject) {
        // 通过 sqlSource 获取 BoundSql 对象，里面包含了 SQL 语句和参数映射
        return sqlSource.getBoundSql(parameterObject);
    }

    /**
     * 将以逗号分隔的字符串转换为字符串数组的工具方法
     *
     * @param in 输入的逗号分隔字符串
     * @return 转换后的字符串数组
     */
    private static String[] delimitedStringToArray(String in) {
        // 如果输入为 null 或去除空格后长度为 0，则返回 null
        if (in == null || in.trim().length() == 0) {
            return null;
        } else {
            // 否则，使用逗号分隔，将字符串拆分为字符串数组并返回
            return in.split(",");
        }
    }
}
